package com.dingding.utils;

import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

@Component
public class RSAUtil {

    private static String LOGIN_PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAPYQzBaYimOB1dQAS7FpFs74LGGChyaneDvoa7bANcItyV5wx6IGSLJE0xeJPEGcBWGbWNWF8K/cMImUdlMr98c+5uWkBbfojs0vvkWFHsxrZ1tVHJF7qybIyrybR5KpD8cwe1PtWKECsZ++AsTELzDizQtsy4vs/uY7aGiqxwlNAgMBAAECgYBr0i5rlEnHkoPM0phYU9EfvdVMoPJxuJ5RueVA/fZQcNm03TxMve4q10ecH5Fcum4NOBHz9i3iEVzfXEOA7Fo0g1eP+sym/GHDqWPThSnNlVmJmGabu/ZH8LNZhWVF7OKTcuLwNpaVeMk7RHixzDtRRPuOjSTpY2CcmNEpWtTUzQJBAP/cBEnxFgXjPd43r0clFw5koLn9WyrswxzuFiYHOsHWeyEwp8ULmfnLW8FI1GgqAII9F3yF4wPCjWuiTaBa5XcCQQD2M2cxMVSPX+IyKO5c4R/R434jjsktk1d58lQZNctodDp53vc0X3958qpPuK323ZFvvInyZghmx56gdRjpAkhbAkBcYqVpzZOWLKd6RcEUf2AD4nHmtoTkyvjoyU9WC+Y7d6aeJWNu0vGWMMfA0vtfYUstu8FvRSjjgqxt4P0GmcuhAkAvPXe8cam3BP/i/gWWnr+7Rzpp2jjx3Ma5KG1IlRDdjwe1YLnwO667GHvjZh1DNAp29SdG8psEFaNzSGSqOPznAkAtq1MFHr2DWQtKtFMOTS7Rka4Hw77wYIhF7jvMhWlKBwVvUaf2gMcybom7SdgrR06wyWIsf1Xt+QdaRuV3L7yr";
    private static String LOGIN_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD2EMwWmIpjgdXUAEuxaRbO+Cxhgocmp3g76Gu2wDXCLclecMeiBkiyRNMXiTxBnAVhm1jVhfCv3DCJlHZTK/fHPublpAW36I7NL75FhR7Ma2dbVRyRe6smyMq8m0eSqQ/HMHtT7VihArGfvgLExC8w4s0LbMuL7P7mO2hoqscJTQIDAQAB";


    private static final String ALGORITHM_RSA = "RSA";

    // Java中RSAPublicKeySpec、X509EncodedKeySpec支持生成RSA公钥
    // 此处使用X509EncodedKeySpec生成
    public static RSAPublicKey getPublicKey(String publicKey) throws Exception {

        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        return (RSAPublicKey) keyFactory.generatePublic(spec);
    }

    // Java中只有RSAPrivateKeySpec、PKCS8EncodedKeySpec支持生成RSA私钥
    // 此处使用PKCS8EncodedKeySpec生成
    public static RSAPrivateKey getPrivateKey(String privateKey) throws Exception {

        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        return (RSAPrivateKey) keyFactory.generatePrivate(spec);
    }

    public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
            throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        // 模长n转换成字节数
        int modulusSize = publicKey.getModulus().bitLength() / 8;
        // PKCS Padding长度为11字节，所以实际要加密的数据不能要 - 11byte
        int maxSingleSize = modulusSize - 11;
        // 切分字节数组，每段不大于maxSingleSize
        byte[][] dataArray = splitArray(data.getBytes(), maxSingleSize);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // 分组加密，并将加密后的内容写入输出字节流
        for (byte[] s : dataArray) {
            out.write(cipher.doFinal(s));
        }
        // 使用Base64将字节数组转换String类型
        return Base64.getEncoder().encodeToString(out.toByteArray());
    }

    public static String encryptByPrivateKey(String data, RSAPrivateKey privateKey)
            throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        // 模长n转换成字节数
        int modulusSize = privateKey.getModulus().bitLength() / 8;
        // PKCS Padding长度为11字节，所以实际要加密的数据不能要 - 11byte
        int maxSingleSize = modulusSize - 11;
        // 切分字节数组，每段不大于maxSingleSize
        byte[][] dataArray = splitArray(data.getBytes(), maxSingleSize);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // 分组加密，并将加密后的内容写入输出字节流
        for (byte[] s : dataArray) {
            out.write(cipher.doFinal(s));
        }
        // 使用Base64将字节数组转换String类型
        return Base64.getEncoder().encodeToString(out.toByteArray());
    }

    /**
     * 私钥解密
     *
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey)
            throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // RSA加密算法的模长 n
        int modulusSize = privateKey.getModulus().bitLength() / 8;
        byte[] dataBytes = data.getBytes();
        // 之前加密的时候做了转码，此处需要使用Base64进行解码
        byte[] decodeData = Base64.getDecoder().decode(dataBytes);
        // 切分字节数组，每段不大于modulusSize
        byte[][] splitArrays = splitArray(decodeData, modulusSize);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        for(byte[] arr : splitArrays){
            out.write(cipher.doFinal(arr));
        }
        return new String(out.toByteArray());
    }

    public static String decryptByPublicKey(String data, RSAPublicKey publicKey)
            throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        // RSA加密算法的模长 n
        int modulusSize = publicKey.getModulus().bitLength() / 8;
        byte[] dataBytes = data.getBytes();
        // 之前加密的时候做了转码，此处需要使用Base64进行解码
        byte[] decodeData = Base64.getDecoder().decode(dataBytes);
        // 切分字节数组，每段不大于modulusSize
        byte[][] splitArrays = splitArray(decodeData, modulusSize);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        for(byte[] arr : splitArrays){
            out.write(cipher.doFinal(arr));
        }
        return new String(out.toByteArray());
    }

    /**
     * 按指定长度切分数组
     *
     * @param data
     * @param len 单个字节数组长度
     * @return
     */
    private static byte[][] splitArray(byte[] data,int len){

        int dataLen = data.length;
        if (dataLen <= len) {
            return new byte[][]{data};
        }
        byte[][] result = new byte[(dataLen-1)/len + 1][];
        int resultLen = result.length;
        for (int i = 0; i < resultLen; i++) {
            if (i == resultLen - 1) {
                int slen = dataLen - len * i;
                byte[] single = new byte[slen];
                System.arraycopy(data, len * i, single, 0, slen);
                result[i] = single;
                break;
            }
            byte[] single = new byte[len];
            System.arraycopy(data, len * i, single, 0, len);
            result[i] = single;
        }
        return result;
    }

    public static String decryptLoginReq(String encrypt) throws Exception{
        return decryptByPrivateKey(encrypt, getPrivateKey(LOGIN_PRIVATE_KEY));
    }

    public static String encrypt(String message) throws Exception {
        RSAPublicKey puk = getPublicKey(LOGIN_PUBLIC_KEY);
        return encryptByPublicKey(message, puk);
    }

    public static String encryptByPrivateKey(String message) throws Exception {
        RSAPrivateKey privateKey = getPrivateKey(LOGIN_PRIVATE_KEY);
        return encryptByPrivateKey(message, privateKey);
    }

    public static String decrypte(String message) throws Exception {
        RSAPrivateKey prk = getPrivateKey(LOGIN_PRIVATE_KEY);
        return decryptByPrivateKey(message, prk);
    }

    public static String decrypteByPublicKey(String message) throws Exception {
        RSAPublicKey publicKey = getPublicKey(LOGIN_PUBLIC_KEY);
        return decryptByPublicKey(message, publicKey);
    }

}
